//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using ManagementStudioServices.Interfaces;
using Microsoft.Research.ScientificWorkflow.ManagementStudioControls.Common;
using Microsoft.Research.ScientificWorkflow.ManagementStudioModels;
using Microsoft.Research.ScientificWorkflow.TridentUtilities;
using Microsoft.Research.ScientificWorkflow.UIDesigner.WpfComposite;
using Microsoft.Practices.Unity;
using SR = Microsoft.Research.DataLayer;
using System.Collections.Generic;
using System;

namespace Microsoft.Research.ScientificWorkflow.ManagementStudioControls.WorkflowManagerModule
{
    /// <summary>
    /// The Presentation logic for the scheduled jobs table.
    /// </summary>
    public class ScheduledJobsListPresenter : IListPresenter, INotifyPropertyChanged
    {
        #region Private variables
        /// <summary>
        /// The unity container instance.
        /// </summary>
        private IUnityContainer container;

        /// <summary>
        /// The workflow manager service instance.
        /// </summary>
        private IWorkflowManagerService workflowManagerService;

        /// <summary>
        /// The search view for the available workflows filter.
        /// </summary>
        private UIElement searchView;

        /// <summary>
        /// The view which displays the workflows.
        /// </summary>
        private UIElement scheduledJobsListView;

        /// <summary>
        /// The Title on the tableview.
        /// </summary>
        private string listViewTitle;

        /// <summary>
        /// The menu items for the listview items.
        /// </summary>
        private Collection<MenuItem> contextMenuItems;

        /// <summary>
        /// The selected item in the listview.
        /// </summary>
        private int selectedIndex;

        /// <summary>
        /// Flag to check if items were retreived in the query. 
        /// </summary>
        private bool emptyList;

        /// <summary>
        /// Holds the current filter.
        /// </summary>
        private string currentFilter;

        private string jobName;
        private string scheduledBy;

        /// <summary>
        /// The items to be displayed on the tableview.
        /// </summary>
        private ObservableCollection<object> items;

        #endregion

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="container">The unity contaier</param>
        /// <param name="view">The center pane view of the presenter.</param>
        /// <param name="service">The workflow manager service</param>
        public ScheduledJobsListPresenter(IUnityContainer container, ScheduledJobsListView view, IWorkflowManagerService service)
        {
            this.container = container;
            this.scheduledJobsListView = view;
            this.workflowManagerService = service;

            this.items = new ObservableCollection<object>();
            this.ListViewTitle = ManagementStudioResourceManager.GetString("TableTitleScheduledJobs");
            this.searchView = this.container.Resolve<ScheduledJobsSearchView>();
            view.DataContext = this;
            (this.searchView as ScheduledJobsSearchView).DataContext = this;

            this.Items.CollectionChanged += Items_CollectionChanged;

            // Initialize Commands.
            this.EditScheduleCommand = new DelegateCommand<ScheduleViewModel>(this.ExecuteEditScheduleCommand, this.WorkflowCommandsCanExecute);
            this.DeleteScheduleCommand = new DelegateCommand<ScheduleViewModel>(this.ExecuteDeleteScheduleCommand, this.WorkflowCommandsCanExecute);
            this.RefreshListCommand = new DelegateCommand<object>(p => { this.DisplayItems(this.currentFilter); });

            //search commmand
            this.SearchCommand = new DelegateCommand<object>(p => this.SearchCommandExecute());

            this.SetContextMenuItems();
        }

        #region Public Properties

        /// <summary>
        /// The command executed when the search button is clicked.
        /// </summary>
        public DelegateCommand<object> SearchCommand { get; private set; }
        /// <summary>
        /// Command to edit the schedule selected.
        /// </summary>
        public DelegateCommand<ScheduleViewModel> EditScheduleCommand { get; private set; }

        /// <summary>
        /// Command to delete the schedule selected.
        /// </summary>
        public DelegateCommand<ScheduleViewModel> DeleteScheduleCommand { get; private set; }

        /// <summary>
        /// The command to refresh the list.
        /// </summary>
        public DelegateCommand<object> RefreshListCommand { get; private set; }

        /// <summary>
        /// Property to check if items were retreived in the query.
        /// </summary>
        public bool EmptyList
        {
            get
            {
                return this.emptyList;
            }
            private set
            {
                this.emptyList = value;
                this.OnNotifyPropertyChanged("EmptyList");
            }
        }
        /// <summary>
        /// Job name for search
        /// </summary>
        public string JobName 
        {
            get 
            {
                return this.jobName;
            }
            set 
            {
                this.jobName = value;
                this.OnNotifyPropertyChanged("JobName");
            }
        }
        /// <summary>
        /// Scheduled by string user name
        /// </summary>
        public string ScheduledBy
        {
            get
            {
                return this.scheduledBy;
            }
            set
            {
                this.scheduledBy = value;
                this.OnNotifyPropertyChanged("ScheduledBy");
            }
        }
        #endregion

        #region IListPresenter Members

        /// <summary>
        /// The items to be displayed in the table view.
        /// </summary>
        public ObservableCollection<object> Items
        {
            get
            {
                return this.items;
            }
        }

        /// <summary>
        /// The title of the table view.
        /// </summary>
        public string ListViewTitle
        {
            get
            {
                return this.listViewTitle;
            }
            set
            {
                this.listViewTitle = value;
                this.OnNotifyPropertyChanged("ListViewTitle");
            }
        }

        /// <summary>
        /// Display the items in the table view.
        /// </summary>
        /// <param name="queryString"></param>
        public void DisplayItems(string queryString)
        {
            try
            {
                Items.Clear();

                SR.JobSchedule.ISearch.ISearchClause clause = null;
                if (!string.IsNullOrEmpty(queryString))
                {
                    this.currentFilter = queryString;
                    if (queryString.Equals(ManagementStudioControlsConstants.RECENTLYSCHEDULEDJOBS, StringComparison.OrdinalIgnoreCase))
                    {
                        DateTime cutoffDate = DateTime.Today.AddDays(-5);
                        clause = SR.JobSchedule.ISearch.CreatedDate(SR.DateTimeField.Condition.AfterOrEqual, cutoffDate);
                    }
                    else if (queryString.Equals(ManagementStudioControlsConstants.JOBSSCHEDULEDBYMEFILTERNAME, StringComparison.OrdinalIgnoreCase))
                    {
                        clause = SR.JobSchedule.ISearch.ScheduledBy(SR.ObjectFieldBase.Condition.Equals, TridentAuthentication.LoggedInUser);
                    }
                }

                // Get the required items.
                Collection<SR.JobSchedule> scheduleList = this.workflowManagerService.GetScheduledJobs(clause);

                // Set the selected Index. Show the empty message if no items found.
                if (scheduleList != null && scheduleList.Count > 0)
                {
                    this.EmptyList = false;

                    // Set status.
                    this.container.Resolve<IStatusMessageControl>().SetText(ManagementStudioResourceManager.GetString("StatusScheduleCount",
                        scheduleList.Count.ToString(CultureInfo.CurrentUICulture)));

                    // Add the items to the list.
                    foreach (SR.JobSchedule schedule in scheduleList)
                    {
                        try
                        {
                            ScheduleViewModel scheduleViewmodel = new ScheduleViewModel(schedule);
                            Items.Add(scheduleViewmodel);
                        }
                        catch (System.Security.SecurityException)
                        {
                            // Ignore: The user might not have permissions on the workflow.
                        }
                    }
                    this.SelectedIndex = 0;

                    this.RaiseCanExecuteEvents();
                }
                else
                {
                    this.EmptyList = true;
                    this.container.Resolve<IStatusMessageControl>().SetText(string.Empty);
                }
            }
            catch (TridentCustomException ex)
            {
                TridentErrorHandler.HandleTridentCustomExceptionInUI(ex);
            }
        }

        /// <summary>
        /// The search view for the available workflows filter.
        /// </summary>
        public UIElement SearchView
        {
            get
            {
                return this.searchView;
            }
        }

        /// <summary>
        /// The view which displays the workflows.
        /// </summary>
        public UIElement ListView
        {
            get
            {
                return this.scheduledJobsListView;
            }
        }

        /// <summary>
        /// The menu items for the listview items.
        /// </summary>
        public Collection<MenuItem> MenuItemCollection
        {
            get
            {
                return this.contextMenuItems;
            }
        }

        /// <summary>
        /// The selected index of the list.
        /// </summary>
        public int SelectedIndex
        {
            get
            {
                return this.selectedIndex;
            }
            set
            {
                this.selectedIndex = value;
                this.OnNotifyPropertyChanged("SelectedIndex");
            }
        }

        #endregion

        #region INotifyPropertyChanged Members

        /// <summary>
        /// The property changed event. Fired when a proprty value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Fires the property changed event.
        /// </summary>
        /// <param name="propertyName">Name of the property which has been modified.</param>
        private void OnNotifyPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion

        #region private methods

        /// <summary>
        /// Delete the selected schedule.
        /// </summary>
        /// <param name="schedule"></param>
        private void ExecuteDeleteScheduleCommand(ScheduleViewModel schedule)
        {
            try
            {
                if (schedule != null)
                {
                    // Get a confirmation from the user.
                    MessageBoxResult result = TridentMessageBox.ShowTridentConfirmationYesNoMessageBox(
                        ManagementStudioResourceManager.GetString("DeleteScheduleConfirmation"));

                    if (result == MessageBoxResult.Yes)
                    {
                        this.workflowManagerService.DeleteSchedule(schedule.ScheduleId);
                        this.items.Remove(schedule);
                    }
                }
            }
            catch (TridentCustomException ex)
            {
                TridentErrorHandler.HandleTridentCustomExceptionInUI(ex);
            }
        }

        /// <summary>
        /// Edit the selected schedule.
        /// </summary>
        /// <param name="schedule"></param>
        private void ExecuteEditScheduleCommand(ScheduleViewModel schedule)
        {
            try
            {
                if (schedule != null)
                {
                    // Open the scheduler window.
                    SchedulerViewPresenter schedulerView = new SchedulerViewPresenter(this.container, schedule.ScheduleId, ScheduleMode.Edit, new SchedulerView(), this.workflowManagerService);

                    // Update the row if the schedule has been modified.
                    if (schedulerView.IsScheduleRegistryModified)
                    {
                        SR.JobSchedule editedSchedule = this.workflowManagerService.GetSchedule(schedule.ScheduleId);
                        ScheduleViewModel editedModel = new ScheduleViewModel(editedSchedule);
                        int index = this.items.IndexOf(schedule);
                        this.items.Remove(schedule);
                        this.items.Insert(index, editedModel);
                        this.SelectedIndex = index;
                    }
                }
            }
            catch (System.Security.SecurityException secExp)
            {
                // Ignore: The user might not have permissions on the workflow.
                TridentMessageBox.ShowTridentErrorMessageBox(secExp.Message);
            }
            catch (TridentCustomException ex)
            {
                TridentErrorHandler.HandleTridentCustomExceptionInUI(ex);
            }

        }

        /// <summary>
        /// Checks if the commands can be executed. It enables and disables the buttons and menuitems.
        /// </summary>
        /// <param name="selectedJob">The job selected.</param>
        /// <returns>True if the command can be executed. False otherwise.</returns>
        private bool WorkflowCommandsCanExecute(ScheduleViewModel selectedJob)
        {
            if (this.EmptyList)
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// The menu items for the listview items.
        /// </summary>
        private void SetContextMenuItems()
        {
            this.contextMenuItems = new Collection<MenuItem>();

            MenuItem editMenuitem = new MenuItem();
            editMenuitem.Header = ManagementStudioResourceManager.GetString("MenuItemHeaderEditSchedule");
            editMenuitem.Command = this.EditScheduleCommand;
            editMenuitem.Icon = IconUtilities.GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ManagementStudio/editschedule.png");
            this.contextMenuItems.Add(editMenuitem);

            MenuItem deleteMenuItem = new MenuItem();
            deleteMenuItem.Header = ManagementStudioResourceManager.GetString("MenuItemHeaderDeleteSchedule");
            deleteMenuItem.Icon = IconUtilities.GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ManagementStudio/deleteschedule.png");
            deleteMenuItem.Command = this.DeleteScheduleCommand;
            this.contextMenuItems.Add(deleteMenuItem);
        }

        /// <summary>
        /// Raise the can execute changed events so that the UI elements can be enabled/disabled.
        /// </summary>
        private void RaiseCanExecuteEvents()
        {
            this.EditScheduleCommand.RaiseCanExecuteChanged();
            this.DeleteScheduleCommand.RaiseCanExecuteChanged();
        }

        /// <summary>
        /// Event handler for the collection changed event on the Items collection.
        /// </summary>
        /// <param name="sender">The source.</param>
        /// <param name="e">Event arguments</param>
        void Items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            ICollection<object> collection = sender as ICollection<object>;

            // If collection is empty then disable the buttons.
            this.EmptyList = (collection.Count == 0);
            this.RaiseCanExecuteEvents();
        }

        /// <summary>
        /// Dummy implementation for the search click button.
        /// </summary>
        private void SearchCommandExecute()
        {
            try
            {
                Items.Clear();

                // This is the place wNodeNamehere the workflows are retrieved from the registry. The search call ideally goes here.
                // Get the required items.
                SR.JobSchedule.ISearch.ISearchClause filter = null;
                List<SR.JobSchedule.ISearch.ISearchClause> filterArray = new List<SR.JobSchedule.ISearch.ISearchClause>();
                if (!string.IsNullOrEmpty(this.JobName))
                {
                    filterArray.Add(SR.JobSchedule.ISearch.Name(SR.StringField.Condition.Contains, this.JobName));
                }

                if (!string.IsNullOrEmpty(this.ScheduledBy))
                {
                    SR.JobSchedule.ISearch.ISearchClause userclause = null;
                    Collection<SR.User> userlist = workflowManagerService.GetAllUsers(SR.User.ISearch.Name(SR.StringField.Condition.Equals, this.ScheduledBy));
                    foreach (SR.User user in userlist)
                    {
                        if (null == userclause)
                        {
                            userclause = SR.JobSchedule.ISearch.ScheduledBy(SR.ObjectFieldBase.Condition.Equals, user);
                        }
                        else
                        {
                            userclause = SR.JobSchedule.ISearch.OR(
                                            SR.JobSchedule.ISearch.ScheduledBy(SR.ObjectFieldBase.Condition.Equals, user),
                                            userclause);
                        }
                    }

                    if (null != userclause)
                    {
                        filterArray.Add(userclause);
                    }
                    else
                    {
                        // Set the empty list property to true. Shows the empty list message and disables the buttons.
                        this.EmptyList = true;
                        this.container.Resolve<IStatusMessageControl>().SetText(string.Empty);
                        return;
                    }
                }

                if (filterArray.Count > 1)
                {
                    filter = SR.JobSchedule.ISearch.AND(filterArray.ToArray());
                }
                else
                {
                    filter = filterArray.Count > 0 ? filterArray[0] : null;
                }

                Collection<SR.JobSchedule> scheduleList = workflowManagerService.GetScheduledJobs(filter);

                // Set the selected Index. Show the empty message if no items found.
                if (scheduleList != null && scheduleList.Count > 0)
                {
                    this.SelectedIndex = 0;
                    this.EmptyList = false;
                    // Set status.
                    this.container.Resolve<IStatusMessageControl>().SetText(ManagementStudioResourceManager.GetString("StatusWorkflowCount",
                        scheduleList.Count.ToString(CultureInfo.CurrentUICulture)));

                    // Add the items to the list.
                    // As the Items property is a observable collection and is bound to the listview. Any changes here will be shown in the listview. 
                    foreach (SR.JobSchedule schedule in scheduleList)
                    {
                        ScheduleViewModel scheduleViewmodel = new ScheduleViewModel(schedule);
                        Items.Add(scheduleViewmodel);
                    }
                }
                else
                {
                    // Set the empty list property to true. Shows the empty list message and disables the buttons.
                    this.EmptyList = true;
                    this.container.Resolve<IStatusMessageControl>().SetText(string.Empty);
                }
                this.RaiseCanExecuteEvents();
            }
            catch (SR.RecordNotFoundException)
            {
                // Set status.
                this.container.Resolve<IStatusMessageControl>().SetText(ManagementStudioResourceManager.GetString("StatusWorkflowCount",
                    0.ToString(CultureInfo.CurrentUICulture)));
            }
            catch (TridentCustomException ex)
            {
                TridentErrorHandler.HandleTridentCustomExceptionInUI(ex);
            }
        }
        #endregion
    }
}
